/*
 * Copyright 2012 The Netty Project
 *
 * The Netty Project licenses this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */
package io.netty.channel;

import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;

import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 一种特殊的 {@link ChannelInboundHandler}, 提供了一种简便的方式来初始化一个 {@link Channel}(一旦它被注册到它的 {@link EventLoop}).
 * <p>
 * 实现最常被用在
 * {@link Bootstrap#handler(ChannelHandler)},
 * {@link ServerBootstrap#handler(ChannelHandler)},
 * {@link ServerBootstrap#childHandler(ChannelHandler)}
 * 上下文中, 来设置一个 {@link Channel} 的 {@link ChannelPipeline}.
 *
 * <pre>
 *
 * public class MyChannelInitializer extends {@link ChannelInitializer} {
 *     public void initChannel({@link Channel} channel) {
 *         channel.pipeline().addLast("myHandler", new MyHandler());
 *     }
 * }
 *
 * {@link ServerBootstrap} bootstrap = ...;
 * ...
 * bootstrap.childHandler(new MyChannelInitializer());
 * ...
 * </pre>
 * 请注意: 该类被标记为 {@link Sharable}, 因此其实现必须可安全地被重用.
 *
 * @param <C> {@link Channel} 子类
 */
@Sharable
public abstract class ChannelInitializer<C extends Channel> extends ChannelInboundHandlerAdapter {

    private static final InternalLogger logger = InternalLoggerFactory.getInstance(ChannelInitializer.class);

    // 我们使用一个 Set 作为 ChannelInitializer, 因为在 Bootstrap / ServerBootstrap 中它通常被所有 Channel 共享.
    // 通过这种方式, 相较于使用 Attributes, 我们可以减少内存使用.
    private final Set<ChannelHandlerContext> initMap = Collections.newSetFromMap(
            new ConcurrentHashMap<ChannelHandlerContext, Boolean>()
    );

    /**
     * 一旦该 {@link Channel} 被注册, 该方法将被调用.
     * <p>
     * 在方法返回后, 该实例将从这个 {@link Channel} 的 {@link ChannelPipeline} 中移除.
     *
     * @param ch the {@link Channel} which was registered.
     * @throws Exception is thrown if an error occurs.
     *                   In that case it will be handled by {@link #exceptionCaught(ChannelHandlerContext, Throwable)}
     *                   which will by default close the {@link Channel}.
     */
    protected abstract void initChannel(C ch) throws Exception;

    @Override
    @SuppressWarnings("unchecked")
    public final void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        // Normally this method will never be called as handlerAdded(...) should call initChannel(...) and remove
        // the handler.
        if (initChannel(ctx)) {
            // we called initChannel(...) so we need to call now pipeline.fireChannelRegistered() to ensure we not
            // miss an event.
            ctx.pipeline().fireChannelRegistered();

            // We are done with init the Channel, removing all the state for the Channel now.
            removeState(ctx);
        } else {
            // Called initChannel(...) before which is the expected behavior, so just forward the event.
            ctx.fireChannelRegistered();
        }
    }

    /**
     * 通过记录日志处理 {@link Throwable}, 同时关闭该 {@link Channel}.
     * 子类可覆盖此方法.
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        if (logger.isWarnEnabled()) {
            logger.warn("Failed to initialize a channel. Closing: " + ctx.channel(), cause);
        }
        ctx.close();
    }

    /**
     * {@inheritDoc} 如果重写该方法, 请确保调用 super
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        if (ctx.channel().isRegistered()) {
            // 在我们当前 DefaultChannelPipeline 实现中, 该结果应该总是为 true.
            // 在 handlerAdded(...) 中调用 initChannel(...) 的好处是, 如果一个 ChannelInitializer 将添加另一个 ChannelInitializer,
            // 这样也不会出现顺序意外. 因为所有的 handlers 都将按预期顺序添加.
            if (initChannel(ctx)) {
                // 初始化 Channel 完成, 移除该 initializer
                removeState(ctx);
            }
        }
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        initMap.remove(ctx);
    }

    @SuppressWarnings("unchecked")
    private boolean initChannel(ChannelHandlerContext ctx) throws Exception {
        // 谨防重入
        if (initMap.add(ctx)) {
            try {
                initChannel((C) ctx.channel());
            } catch (Throwable cause) {
                // Explicitly call exceptionCaught(...) as we removed the handler before calling initChannel(...).
                // We do so to prevent multiple calls to initChannel(...).
                exceptionCaught(ctx, cause);
            } finally {
                // 从 pipeline 中移除该初始化器
                ChannelPipeline pipeline = ctx.pipeline();
                if (pipeline.context(this) != null) {
                    pipeline.remove(this);
                }
            }
            return true;
        }
        return false;
    }

    private void removeState(final ChannelHandlerContext ctx) {
        // 如果我们使用的 EventExecutor 做了一些特别的(funky)事情, removal 可能会以异步形式发生
        if (ctx.isRemoved()) {
            initMap.remove(ctx);
        } else {
            // The context is not removed yet which is most likely the case because a custom EventExecutor is used.
            // Let's schedule it on the EventExecutor to give it some more time to be completed in case it is offloaded.
            ctx.executor().execute(new Runnable() {
                @Override
                public void run() {
                    initMap.remove(ctx);
                }
            });
        }
    }
}
